/*
 * @brief:
 * @Author: TzzzT
 * @Date: 2022-05-08 11:32:55
 * @LastEditTime: 2023-05-05 21:23:11
 * @LastEditors: TzzzT
 */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include "driver/i2c.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
// #include "unity.h"

#include "esp_log.h"

#include "RCS_scr.h"
#include "RCS_slave.h"

#include "rcs_ui.h"

////no connect////

#define NO_CONNECT_X 16
#define NO_CONNECT_Y 24

////wait start////
#define IMEI_X 20
#define DEVICE_X 20
#define WAIT_START_BATTERY_X 20
#define IMEI_Y 4
#define DEVICE_Y 24
#define BATTERY_Y 44
#define SIZE_2 16
////gaming////
#define LINE_1 1
#define SIZE_12 12
#define SIZE_12_Y SIZE_12
#define SIZE_12_X (SIZE_12 / 2)
#define ATK_X 1
#define TEAM_ROOM_X 64
#define DEVICE_NUM_X 96

#define HP_X 1
#define HP_Y 16

#define LINE_SCORE 17

#define SCORE_X 50
// 49 + (8*3 + 1)
#define KILL_X 75
#define DEATH_X 100

#define SIZE_16 16
#define SIZE_16_X (SIZE_16 / 2)

#define HINT_X SCORE_X
#define HINT_Y (HP_Y + SIZE_16 - 2)

#define LINE_2 (HP_Y + 32 + 1)

#define LINE_BULLET 51
#define BULLET_BAR_START_X 10
#define MAGAZINE_X 81
#define SPLIT_X 94
#define ALL_BULLET_X 101

////dead////
#define DEAD_X 0
#define DEAD_Y 20

static const char *TAG = "ssd1306";

// static const uint8_t dead_img[512];
// static const uint8_t dead_convert_img[512];

// void clean_scr(void){

// }

#define I2C_MASTER_SCL_IO 6       /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO 7       /*!< gpio number for I2C master data  */
#define I2C_MASTER_NUM I2C_NUM_0  /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */
/**
 * @brief i2c master initialization
 */
static void i2c_bus_init(void)
{
    i2c_config_t conf;
    conf.mode = I2C_MODE_MASTER;
    conf.sda_io_num = (gpio_num_t)I2C_MASTER_SDA_IO;
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_io_num = (gpio_num_t)I2C_MASTER_SCL_IO;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
    conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL;

    i2c_param_config(I2C_MASTER_NUM, &conf);
    i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
}

static void i2c_bus_delete(void)
{
    esp_err_t ret;
    ret = i2c_driver_delete(I2C_MASTER_NUM);
}

int esp32_i2c_write(uint8_t addr, uint32_t idx, uint8_t *data)
{
    int ret;
    i2c_cmd_handle_t handler = i2c_cmd_link_create();
    i2c_master_start(handler);
    i2c_master_write_byte(handler, addr | I2C_MASTER_WRITE, true);
    i2c_master_write(handler, data, idx, true);
    i2c_master_stop(handler);
    ret = i2c_master_cmd_begin(I2C_NUM_0, handler, 100);
    i2c_cmd_link_delete(handler);
    return ret;
}

// port u8g2
#include "u8g2.h"
uint8_t _u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    switch (msg)
    {
    case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
        // ets_delay_us(arg_int/10);
        break;
    case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
        // ets_delay_us(arg_int * 10);
        break;
    case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
        // vTaskDelay(1000/portTICK_RATE_MS); //在这里既然使用了freertos的vTaskDelay，那么u8g2必须在进程上下文运行
        break;
    case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
        // ets_delay_us(10 / arg_int);
        break;                    // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
    case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
        if (arg_int == 1)
        {
            gpio_set_level(I2C_MASTER_SCL_IO, 1);
        }
        else if (arg_int == 0)
        {
            gpio_set_level(I2C_MASTER_SCL_IO, 0);
        }
        break;                   // arg_int=1: Input dir with pullup high for I2C clock pin
    case U8X8_MSG_GPIO_I2C_DATA: // arg_int=0: Output low at I2C data pin
        if (arg_int == 1)
        {
            gpio_set_level(I2C_MASTER_SDA_IO, 1);
        }
        else if (arg_int == 0)
        {
            gpio_set_level(I2C_MASTER_SDA_IO, 0);
        }
        break; // arg_int=1: Input dir with pullup high for I2C data pin
    default:
        u8x8_SetGPIOResult(u8x8, 1); // default return value
        break;
    }
    return 1;
}

uint8_t _u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    static uint8_t buffer[32]; /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
    static uint8_t buf_idx;
    uint8_t *data;

    switch (msg)
    {
    case U8X8_MSG_BYTE_SEND:
        data = (uint8_t *)arg_ptr;
        while (arg_int > 0)
        {
            buffer[buf_idx++] = *data;
            data++;
            arg_int--;
        }
        break;
    case U8X8_MSG_BYTE_INIT:
        /* add your custom code to init i2c subsystem */
        break;
    case U8X8_MSG_BYTE_SET_DC:
        /* ignored for i2c */
        break;
    case U8X8_MSG_BYTE_START_TRANSFER:
        buf_idx = 0;
        break;
    case U8X8_MSG_BYTE_END_TRANSFER:
        esp32_i2c_write(u8x8_GetI2CAddress(u8x8), buf_idx, buffer);
        break;
    default:
        return 0;
    }
    return 1;
}

static esp_err_t check_if_hw_connect(void)
{
    i2c_bus_init();
#define SSD1306_I2C_ADDRESS ((uint8_t)0x3C)
#define SSD1306_WRITE_CMD (0x00)
    uint8_t dev_addr = SSD1306_I2C_ADDRESS << 1;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    esp_err_t ret = i2c_master_start(cmd);
    assert(ESP_OK == ret);
    ret = i2c_master_write_byte(cmd, dev_addr | I2C_MASTER_WRITE, true);
    assert(ESP_OK == ret);
    ret = i2c_master_write_byte(cmd, SSD1306_WRITE_CMD, true);
    assert(ESP_OK == ret);
    uint8_t data = 0xAF; //--turn on oled panel
    ret = i2c_master_write(cmd, &data, 1, true);
    assert(ESP_OK == ret);
    ret = i2c_master_stop(cmd);
    assert(ESP_OK == ret);
    ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    if (ret != ESP_OK)
    {
        return ESP_FAIL;
    }
    else
    {
        return ESP_OK;
    }
}

extern rcs_slave_system_t rcsSystem;

display_t rcsDisplay = {
    .batt = {-1, -1, -1, -1},
    .conn_dev_num = 1,
    .bullet_num = 0,
    .hint = "hello",

    .dev_id = 123456,

    .attack = 100,
    .teamroom = 12,
    .low_batt = false,

    .kill = 5,
    .death = 2,
    .score = 30,
};
u8g2_t u8g2;

void rcs_screen_init_u8g2(void)
{
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R2, _u8x8_byte_hw_i2c, _u8x8_gpio_and_delay);
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);
    ESP_LOGE(TAG, "init ok");
}

esp_err_t scr_init(void)
{
    esp_err_t ret = check_if_hw_connect();
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "no ssd1306");
        return ret;
    }
    else
    {
        ESP_LOGE(TAG, "has ssd1306, init u8g2");
        rcs_screen_init_u8g2();
        ret = ESP_OK;
    }
    return ret;
}

void position_update(uint8_t *cur_pos, uint8_t tar_pos, uint8_t speed)
{
    uint8_t diff = abs(tar_pos - *cur_pos);
    if (diff == 0)
    {
        return;
    }
    speed = diff / 5 + 1;

    speed = (diff > speed) ? speed : 1;
    if (tar_pos > *cur_pos)
    {
        *cur_pos += speed;
    }
    else
    {
        *cur_pos -= speed;
    }
    // ESP_LOGE(TAG, "diff:%d tar:%d cur:%d speed:%d", diff, tar_pos, *cur_pos, speed);
}

void goto_new_page(uint8_t *scr_page)
{
    if (rcsSystem.device.is_connect_master == CONNECTED_BLE)
    {
        if (rcsSystem.game.start)
        {
            *scr_page = SCR_LIVING_PAGE;
        }
        else
        {
            *scr_page = SCR_WAITING_PAGE;
        }
    }
    else
    {
        *scr_page = SCR_DISCONNET_PAGE;
    }
}

char company_name[10] = "WENBENTEC";
font_t logo_font = {
    .class = u8g2_font_t0_22b_tf,
    .height = 16,
    .width = 11,
};

bool start_page_ui(uint16_t *count, uint8_t *scr_page)
{
    static uint8_t logo_x = 14;
    // 40y在正中间
    static uint8_t logo_y = 40;
    u8g2_ClearBuffer(&u8g2);
    u8g2_SetFont(&u8g2, logo_font.class);
    uint8_t disappear_idx;
    uint8_t company_len = strlen(company_name);

    // 先÷5，让数字变换的速度减少5倍，再%9，得到9个频率相等的数字
    disappear_idx = (*count) / 2 % (company_len + 8);
    if (disappear_idx < company_len)
    {
        uint8_t i = 0;
        char disp[2] = "_";
        for (i = 0; i < company_len; i++)
        {
            if (i == disappear_idx)
            {
                disp[0] = ' ';
            }
            else
            {
                disp[0] = company_name[i];
            }
            u8g2_DrawUTF8(&u8g2, logo_x + i * logo_font.width, logo_y, disp);
        }
    }
    else
    {
        if (disappear_idx % 2)
        {
            u8g2_DrawUTF8(&u8g2, logo_x, logo_y, company_name);
        }
    }
    u8g2_SendBuffer(&u8g2);
    return (disappear_idx == company_len + 8 - 1) ? false : true;
}

char disconnet[10] = "DISCONNET";
void disconnect_page_ui(uint16_t *count, uint8_t *scr_page)
{
    static uint8_t logo_x = 14;
    // 40y在正中间
    static uint8_t logo_y = 40;
    u8g2_ClearBuffer(&u8g2);
    u8g2_SetFont(&u8g2, logo_font.class);

    u8g2_DrawUTF8(&u8g2, logo_x, logo_y, disconnet);
    u8g2_SendBuffer(&u8g2);
    return;
}

char waitgame_batt_str[4][4] = {
    "M--",
    "T--",
    "S--",
    "R--",
};
uint8_t dev_batt_x[4] = {4, 36, 68, 100};
#define WAITGAME_BATTERY_Y 12

#define WAITGAME_SPLIT_Y1 13

#define WAITGAME_WAIT_X -5
#define WAITGAME_WAIT_Y 32

#define WAITGAME_DEV_ID_X 30
#define WAITGAME_DEV_ID_Y 52

#define WAITGAME_SPLIT_Y2 55

char wait_str[] = "   WAITING   ";
char dev_id_str[] = "123456";
void wait_game_start_page_ui(uint16_t *count, uint8_t *scr_page)
{
    u8g2_ClearBuffer(&u8g2);
    u8g2_SetFont(&u8g2, u8g2_font_t0_16_mf);
    for (int i = 0; i < 4; i++)
    {
        // 显示0表示连上了但是设备没接电池，--代表没连上
        // esp_log_buffer_hex("SCR", &rcsDisplay.batt[0], 4);
        if (rcsDisplay.batt[i] >= 0)
        {
            if (rcsDisplay.batt[i] >= 100)
            {
                rcsDisplay.batt[i] = 99;
            }
            sprintf(&waitgame_batt_str[i][1], "%2d", rcsDisplay.batt[i]);
        }
        else
        {
            sprintf(&waitgame_batt_str[i][1], "--");
        }
        u8g2_DrawUTF8(&u8g2, dev_batt_x[i], WAITGAME_BATTERY_Y, waitgame_batt_str[i]);
    }
    u8g2_DrawHLine(&u8g2, 0, WAITGAME_SPLIT_Y1, 128);
    u8g2_SetFont(&u8g2, u8g2_font_t0_22b_me);
    u8g2_SetDrawColor(&u8g2, 0);
    u8g2_DrawUTF8(&u8g2, WAITGAME_WAIT_X, WAITGAME_WAIT_Y, wait_str);
    u8g2_SetDrawColor(&u8g2, 1);
    u8g2_SetFont(&u8g2, u8g2_font_t0_22b_tn);
    rcsDisplay.dev_id = rcsSystem.game.player.id;
    sprintf(dev_id_str, "%06"PRIu32, rcsDisplay.dev_id);
    u8g2_DrawUTF8(&u8g2, WAITGAME_DEV_ID_X, WAITGAME_DEV_ID_Y, dev_id_str);

    u8g2_DrawHLine(&u8g2, 0, WAITGAME_SPLIT_Y2, 128);

#define _BLOCK_WIDTH 32
#define _BLOCK_LENGTH (128 - _BLOCK_WIDTH)

    if (!rcsSystem.device.is_net_up)
    {
        uint8_t des = *count % (_BLOCK_LENGTH + 1);
        static uint8_t move_flag = 1;
        uint8_t _start_des = (move_flag) ? des : (_BLOCK_LENGTH - des);

        for (int j = _start_des; j < _start_des + _BLOCK_WIDTH; j += 2)
        {
            u8g2_DrawVLine(&u8g2, j, WAITGAME_SPLIT_Y2 + 2, 12);
        }
        if (des >= _BLOCK_LENGTH)
        {
            move_flag = !move_flag;
        }
    }
    else
    {
        for (int j = 0; j < 128; j += 2)
        {
            u8g2_DrawVLine(&u8g2, j, WAITGAME_SPLIT_Y2 + 2, 12);
        }
    }

    u8g2_SendBuffer(&u8g2);
    return;
}

#define GAMELIVING_ATTACK_X 0
#define GAMELIVING_TEAMROOM_X 45
#define GAMELIVING_BATTERY_X 96

#define GAMELIVING_LINE1_Y 12

#define GAMELIVING_SPLIT_Y1 14

#define GAMELIVING_HP_X 0
#define GAMELIVING_HP_Y 48

#define GAMELIVING_K_X 58
#define GAMELIVING_D_X 83
#define GAMELIVING_S_X 107
#define GAMELIVING_KDS_Y 30

#define GAMELIVING_HINT_X 60
#define GAMELIVING_HINT_Y 45

#define GAMEDEAD_HINT_X 21
#define GAMEDEAD_HINT_Y 45

#define GAMELIVING_SPLIT_Y2 51

#define GAMELIVING_BULLET_X 100
#define GAMELIVING_BULLET_Y 64

static char game_living_HP_str[6] = {0};

static char attack_str[7] = {0};
static char teamroom_str[6] = {0};
static char battery_str[6] = "B:--";

static char kill_str[6] = {0};
static char death_str[6] = {0};
static char score_str[6] = {0};

char bullet_str[6] = {0};
char dead_str[5] = "DEAD";
void game_living_page_ui(uint16_t *count, uint8_t *scr_page)
{
    // ESP_LOGE(TAG, "start");
    u8g2_ClearBuffer(&u8g2);
    u8g2_SetDrawColor(&u8g2, 1);
    u8g2_SetFont(&u8g2, u8g2_font_saikyosansbold8_8u);

    rcsDisplay.attack = rcsSystem.game.player.damage;
    rcsDisplay.teamroom = rcsSystem.game.player.team * 10 + rcsSystem.game.player.room;

    sprintf(attack_str, "A:%03d", rcsDisplay.attack);
    u8g2_DrawUTF8(&u8g2, GAMELIVING_ATTACK_X, GAMELIVING_LINE1_Y, attack_str);
    sprintf(teamroom_str, "T:%03d",(uint8_t)(rcsDisplay.teamroom * 10 + rcsDisplay.conn_dev_num));
    u8g2_DrawUTF8(&u8g2, GAMELIVING_TEAMROOM_X, GAMELIVING_LINE1_Y, teamroom_str);

    if (rcsDisplay.batt[4] < 30)
    {
        esp_log_buffer_hex("batt:", rcsDisplay.batt, 5);
        char _s[] = "B:LO";
        memcpy(battery_str, _s, 4);
    }
    else
    {
        sprintf(battery_str, "B:%02d", rcsDisplay.batt[4]);
    }

    u8g2_DrawUTF8(&u8g2, GAMELIVING_BATTERY_X, GAMELIVING_LINE1_Y, battery_str);
    u8g2_DrawHLine(&u8g2, 0, GAMELIVING_SPLIT_Y1, 128);

    rcsDisplay.HP = rcsSystem.game.player.hp;
    rcsDisplay.kill = rcsSystem.game.player.kill;
    rcsDisplay.death = rcsSystem.game.player.death;
    rcsDisplay.score = rcsSystem.game.player.team_score;
    if (rcsSystem.game.player.living)
    {
        sprintf(game_living_HP_str, "%03d", rcsDisplay.HP);
        u8g2_SetFont(&u8g2, u8g2_font_logisoso30_tn);
        u8g2_DrawUTF8(&u8g2, GAMELIVING_HP_X, GAMELIVING_HP_Y, game_living_HP_str);

        u8g2_SetFont(&u8g2, u8g2_font_7x14B_tf);
        sprintf(kill_str, "K%02d", rcsDisplay.kill);
        u8g2_DrawUTF8(&u8g2, GAMELIVING_K_X, GAMELIVING_KDS_Y, kill_str);
        sprintf(death_str, "D%02d", rcsDisplay.death);
        u8g2_DrawUTF8(&u8g2, GAMELIVING_D_X, GAMELIVING_KDS_Y, death_str);
        sprintf(score_str, "S%02d", rcsDisplay.score);
        u8g2_DrawUTF8(&u8g2, GAMELIVING_S_X, GAMELIVING_KDS_Y, score_str);

        u8g2_DrawBox(&u8g2, 58, 32, 70, 16);
        // u8g2_SetFont(&u8g2, u8g2_font_luBS12_tr);
        u8g2_SetDrawColor(&u8g2, 0);
        u8g2_DrawUTF8(&u8g2, GAMELIVING_HINT_X, GAMELIVING_HINT_Y, rcsDisplay.hint);
    }
    else
    {
        u8g2_SetFont(&u8g2, u8g2_font_fub25_tf);
        int mode = (*count) / 5 % 2;
        if (mode)
        {
            u8g2_DrawBox(&u8g2, 0, 16, 128, 32);
            u8g2_SetDrawColor(&u8g2, 0);
            u8g2_DrawUTF8(&u8g2, GAMEDEAD_HINT_X, GAMEDEAD_HINT_Y, dead_str);
        }
        else
        {
            u8g2_SetDrawColor(&u8g2, 1);
            u8g2_DrawUTF8(&u8g2, GAMEDEAD_HINT_X, GAMEDEAD_HINT_Y, dead_str);
        }
    }

    u8g2_SetDrawColor(&u8g2, 1);
    u8g2_DrawHLine(&u8g2, 0, GAMELIVING_SPLIT_Y2, 128);

    // uint8_t disappear_idx = (*count) / 2 % 2;
    for (int j = 0; j < GAMELIVING_BULLET_X; j += 2)
    {
        u8g2_DrawVLine(&u8g2, j, GAMELIVING_SPLIT_Y2 + 2, 12);
    }

    rcsDisplay.bullet_num = rcsSystem.game.player.bullet_nums;
    sprintf(bullet_str, "%03d", rcsDisplay.bullet_num);
    u8g2_SetFont(&u8g2, u8g2_font_t0_16b_tn);
    u8g2_DrawUTF8(&u8g2, GAMELIVING_BULLET_X + 2, GAMELIVING_BULLET_Y, bullet_str);
    u8g2_SendBuffer(&u8g2);
    return;
}

// void test_font(void){
//     u8g2_ClearDisplay(&u8g2);
//     u8g2_SetFont(&u8g2,u8g2_font_t0_22b_tf);
//     //
//     u8g2_DrawUTF8(&u8g2, 30, 40, "RCS-SHOOT");
//     u8g2_SendBuffer(&u8g2);
// }

// /// @brief test
// uint8_t edge_logo_coop[4][2] = {
//     {0, 40},
//     {30, 20},
//     {60, 40},
//     {30, 60}
// };
// void start_page_ui(uint16_t* count, uint8_t* scr_page){
//     static uint8_t next_coop_idx = 0;
//     static uint8_t logo_x = 0;
//     static uint8_t logo_y = 40;
//     u8g2_ClearBuffer(&u8g2);
//     if(logo_x == edge_logo_coop[next_coop_idx][0] &&
//     logo_y == edge_logo_coop[next_coop_idx][1]){
//         next_coop_idx++;
//         if(next_coop_idx == 4){
//             next_coop_idx = 0;
//         }
//     }
//     position_update(&logo_x, edge_logo_coop[next_coop_idx][0], 3);
//     position_update(&logo_y, edge_logo_coop[next_coop_idx][1], 2);
//     ESP_LOGE(TAG, "x:%d tarx:%d y:%d tary:%d", logo_x, edge_logo_coop[next_coop_idx][0],
//                             logo_y, edge_logo_coop[next_coop_idx][1]);
//     u8g2_SetFont(&u8g2,u8g2_font_maniac_te);
//     u8g2_DrawUTF8(&u8g2, logo_x, logo_y, "L R S");
//     u8g2_SendBuffer(&u8g2);
//     return;
// }
